延續之前實作的Day14 延續前實戰新增 FAB + Bottom App Bar 實作後,這次將實現Day15~Day18文章介紹MDC的 Bottom sheets、Dialogs、Divider、Menus的實作
model_bottom_sheet_content
ModalBottomSheet
<item name="behavior_draggable">false</item>
<item name="backgroundTint">@color/white</item>
model_bottom_sheet_content
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/modelBottomSheet"
style="?attr/bottomSheetDialogTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<!-- Bottom sheet contents. -->
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="篩選"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
style="@style/Widget.Material3.Button.IconButton"
app:iconTint="@color/black"
app:icon="@drawable/ic_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/divider1"/>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:dividerColor="@color/gray"
app:dividerInsetEnd="20dp"
app:dividerInsetStart="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView1" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="20dp"
android:text="類型"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView1" />
<!--CheckBox-->
<TextView
android:id="@+id/tv_check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="20dp"
android:text="CheckBox"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@id/textView2"
app:layout_constraintTop_toBottomOf="@id/textView2"/>
<CheckBox
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/divider2" />
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:dividerColor="@color/gray"
app:dividerInsetEnd="20dp"
app:dividerInsetStart="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_check_box"/>
<!--Switch-->
<TextView
android:id="@+id/tv_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="20dp"
android:text="Switch"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@id/textView2"
app:layout_constraintTop_toBottomOf="@id/divider2"/>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:dividerColor="@color/gray"
app:dividerInsetEnd="20dp"
app:dividerInsetStart="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_switch"/>
<!--Radio button-->
<TextView
android:id="@+id/tv_radio_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="20dp"
android:text="Radio button"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@id/textView2"
app:layout_constraintTop_toBottomOf="@id/divider3"/>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:dividerColor="@color/gray"
app:dividerInsetEnd="20dp"
app:dividerInsetStart="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_radio_button"/>
<!--Progress-->
<TextView
android:id="@+id/tv_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="20dp"
android:text="Radio button"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@id/textView2"
app:layout_constraintTop_toBottomOf="@id/divider4"/>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:dividerColor="@color/gray"
app:dividerInsetEnd="20dp"
app:dividerInsetStart="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_progress"/>
</androidx.constraintlayout.widget.ConstraintLayout>
ModalBottomSheet
class ModalBottomSheet : BottomSheetDialogFragment() {
companion object {
const val TAG = "ModalBottomSheet"
}
private var _binding: ModelBottomSheetContentBinding? = null
private val binding get() = _binding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ModelBottomSheetContentBinding.inflate(inflater, container, false)
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 自定義關閉
binding?.btnClose?.setOnClickListener{
dismiss()
}
}
}
style="?attr/bottomSheetDialogTheme"
<style name="Theme.MDCSubject" parent="Theme.Material3.Light.NoActionBar">
....
<!-- Customize your theme here. -->
<item name="bottomSheetDialogTheme">@style/ModalBottomSheetDialog</item>
</style>
<!-- Customize -->
<style name="ModalBottomSheet" parent="Widget.Material3.BottomSheet.Modal">
<item name="behavior_draggable">false</item>
<item name="backgroundTint">@color/white</item>
</style>
<style name="ModalBottomSheetDialog" parent="ThemeOverlay.Material3.BottomSheetDialog">
<item name="bottomSheetStyle">@style/ModalBottomSheet</item>
</style>
fragment_product_cards.xml
BottomAppBar
的menu新增用戶選項full_screen_dialog_fragment.xml
FullScreenDialogFragment.kt
ProductCardsFragment.kt
fragment_product_cards.xml
<item
android:id="@+id/account"
android:icon="@drawable/ic_account"
android:title="用戶"
app:showAsAction="ifRoom" />
full_screen_dialog_fragment.xml
style="@style/Widget.Material3.Button.TextButton"
icon - 用戶的大頭貼
Filled text field - 稱呼
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:hint="稱呼"
android:textColorHint="@color/dark_gray"
app:boxBackgroundColor="@color/white"
app:endIconMode="clear_text"
Outlined text field - 信箱
android:hint="信箱"
android:textColorHint="@color/dark_gray"
app:endIconMode="clear_text"
Outlined text field - 帳號
android:hint="帳號"
android:textColorHint="@color/dark_gray"
app:endIconMode="clear_text"
Outlined text field - 密碼
android:hint="密碼"
android:textColorHint="@color/dark_gray"
app:endIconMode="password_toggle"
Text field的選單ExposedDropdownMenu
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
full_screen_dialog_fragment.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<!--Header -->
<!--Icon-->
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_close" />
<!--Headline -->
<TextView
android:id="@id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:text="Full-screen dialog title"
android:textSize="20dp"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@id/imageView" />
<!--Text button -->
<Button
android:id="@+id/button"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save"
app:layout_constraintBottom_toBottomOf="@id/tvTitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tvTitle" />
<!--Divider (optional)-->
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:dividerColor="@color/gray"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button" />
<!-- icon - 用戶的大頭貼 -->
<ImageView
android:id="@+id/imageUser"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider1"
app:srcCompat="@drawable/ic_account" />
<!--Filled text field - 稱呼-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputName"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:hint="稱呼"
android:textColorHint="@color/dark_gray"
app:boxBackgroundColor="@color/white"
app:endIconMode="clear_text"
app:layout_constraintBottom_toBottomOf="@id/imageUser"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/imageUser"
app:layout_constraintTop_toTopOf="@id/imageUser">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<!--Outlined text field - 信箱-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:hint="信箱"
android:textColorHint="@color/dark_gray"
app:endIconMode="clear_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/imageUser">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<!--Outlined text field - 帳號-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputAccountNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:hint="帳號"
android:textColorHint="@color/dark_gray"
app:endIconMode="clear_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textInputEmail"
app:startIconDrawable="@drawable/ic_person">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<!--Outlined text field - 密碼-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:hint="密碼"
android:textColorHint="@color/dark_gray"
app:endIconMode="password_toggle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textInputAccountNumber"
app:startIconDrawable="@drawable/ic_lock">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
app:dividerColor="@color/gray"
app:dividerInsetEnd="20dp"
app:dividerInsetStart="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textInputPassword" />
<TextView
android:id="@+id/tvTheme"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:text="居住"
android:textSize="18dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider2" />
<!--OutlinedBox.ExposedDropdownMenu - 國家-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputCountry"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:hint="國家"
app:layout_constraintEnd_toStartOf="@+id/textInputCity"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvTheme">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
<!--OutlinedBox.ExposedDropdownMenu - 城市-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputCity"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:hint="城市"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/textInputCountry"
app:layout_constraintTop_toBottomOf="@id/tvTheme">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
FullScreenDialogFragment.kt
class FullScreenDialogFragment : DialogFragment() {
private var _binding: FullScreenDialogFragmentBinding? = null
private val binding get() = _binding
override fun onCreate(savedInstanceState: Bundle?) {
val dialog = super.onCreateDialog(savedInstanceState)
val window = dialog.window
window?.requestFeature(Window.FEATURE_NO_TITLE)
window?.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT)
setStyle(STYLE_NORMAL, android.R.style.Theme_Light_NoTitleBar_Fullscreen)
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FullScreenDialogFragmentBinding.inflate(layoutInflater, container, false)
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
}
private fun initView() {
// 關閉dialog
binding?.btnClose?.setOnClickListener {
dialog?.dismiss()
}
// 實作儲存
binding?.btnSave?.setOnClickListener {
Toast.makeText(context, "實作儲存", Toast.LENGTH_LONG).show()
}
// 範例國家Menu
val exampleCountryItem = arrayOf("Item 1", "Item 2", "Item 3", "Item 4")
(binding?.textInputCountry?.editText as? MaterialAutoCompleteTextView)?.setSimpleItems(exampleCountryItem)
// 範例城市Menu
val exampleCityItems = arrayOf("Item 1", "Item 2", "Item 3", "Item 4")
(binding?.textInputCity?.editText as? MaterialAutoCompleteTextView)?.setSimpleItems(exampleCityItems)
}
}
BottomAppBar
的OnMenuItemClick點擊剛剛新增的item
private inner class OnMenuItemClick : androidx.appcompat.widget.Toolbar.OnMenuItemClickListener {
override fun onMenuItemClick(item: MenuItem?): Boolean {
return when (item?.itemId) {
R.id.mainPage -> {
......
true
}
R.id.collect -> {
......
true
}
R.id.account -> {
// 使用者資料點擊開啟Full-screen dialogs
if(activity != null) {
requireActivity().supportFragmentManager.let {
FullScreenDialogFragment().show(it, "")
}
}
true
}
else -> false
}
}
}
以上是這次的實作內容 歡迎下載程式碼參考
感謝您看到這邊
參考資料:
Day15 使用M3的Bottom sheets
Day16 使用M3的Dialogs
Day17 使用M3的Divider
Day18 使用M3的Menus